﻿using System;
using GeoAPI.Geometries;
using YJKGridTopologyCal.Geometries;

namespace YJKGridTopologyCal.Operation.Overlay.Snap
{
	public class LineStringSnapper
	{
		public LineStringSnapper(ILineString srcLine, double snapTolerance) : this(srcLine.Coordinates, snapTolerance)
		{
		}

		public LineStringSnapper(Coordinate[] srcPts, double snapTolerance)
		{
			this._srcPts = srcPts;
			this._isClosed = LineStringSnapper.IsClosed(this._srcPts);
			this._snapTolerance = snapTolerance;
		}

		public bool AllowSnappingToSourceVertices
		{
			get
			{
				return this._allowSnappingToSourceVertices;
			}
			set
			{
				this._allowSnappingToSourceVertices = value;
			}
		}

		private static bool IsClosed(Coordinate[] pts)
		{
			return pts.Length > 1 && pts[0].Equals2D(pts[pts.Length - 1]);
		}

		public Coordinate[] SnapTo(Coordinate[] snapPts)
		{
			CoordinateList coordinateList = new CoordinateList(this._srcPts);
			this.SnapVertices(coordinateList, snapPts);
			this.SnapSegments(coordinateList, snapPts);
			return coordinateList.ToCoordinateArray();
		}

		private void SnapVertices(CoordinateList srcCoords, Coordinate[] snapPts)
		{
			int num = this._isClosed ? (srcCoords.Count - 1) : srcCoords.Count;
			for (int i = 0; i < num; i++)
			{
				Coordinate pt = srcCoords[i];
				Coordinate coordinate = this.FindSnapForVertex(pt, snapPts);
				if (coordinate != null)
				{
					srcCoords[i] = new Coordinate(coordinate);
					if (i == 0 && this._isClosed)
					{
						srcCoords[srcCoords.Count - 1] = new Coordinate(coordinate);
					}
				}
			}
		}

		private Coordinate FindSnapForVertex(Coordinate pt, Coordinate[] snapPts)
		{
			foreach (Coordinate coordinate in snapPts)
			{
				if (pt.Equals2D(coordinate))
				{
					return null;
				}
				if (pt.Distance(coordinate) < this._snapTolerance)
				{
					return coordinate;
				}
			}
			return null;
		}

		private void SnapSegments(CoordinateList srcCoords, Coordinate[] snapPts)
		{
			if (snapPts.Length == 0)
			{
				return;
			}
			int num = snapPts.Length;
			if (snapPts[0].Equals2D(snapPts[snapPts.Length - 1]))
			{
				num = snapPts.Length - 1;
			}
			for (int i = 0; i < num; i++)
			{
				Coordinate coordinate = snapPts[i];
				int num2 = this.FindSegmentIndexToSnap(coordinate, srcCoords);
				if (num2 >= 0)
				{
					srcCoords.Add(num2 + 1, new Coordinate(coordinate), false);
				}
			}
		}

		private int FindSegmentIndexToSnap(Coordinate snapPt, CoordinateList srcCoords)
		{
			double num = double.MaxValue;
			int result = -1;
			for (int i = 0; i < srcCoords.Count - 1; i++)
			{
				this._seg.P0 = srcCoords[i];
				this._seg.P1 = srcCoords[i + 1];
				if (this._seg.P0.Equals2D(snapPt) || this._seg.P1.Equals2D(snapPt))
				{
					if (!this._allowSnappingToSourceVertices)
					{
						return -1;
					}
				}
				else
				{
					double num2 = this._seg.Distance(snapPt);
					if (num2 < this._snapTolerance && num2 < num)
					{
						num = num2;
						result = i;
					}
				}
			}
			return result;
		}

		private readonly double _snapTolerance;

		private readonly Coordinate[] _srcPts;

		private readonly LineSegment _seg = new LineSegment();

		private bool _allowSnappingToSourceVertices;

		private readonly bool _isClosed;
	}
}
